/**
 * Created by tanbingqin on 2020/1/9.
 */
import { messages, EventUtils } from "share/common";
import React from "react";
import ReactDOM from "react-dom";
import { Icon, Button, Row, Col } from "antd";
import "styles/components/template/comment/comment.scss";
import ListSelector from "components/list-selector";

/**
 * ps list
 * 1.删除的时候名字@符整块一起删除  已解决 塞入节点的contentEditable设置为false
 * 2.只支持纯文本输入，不允许粘贴图片啥的  部分解决 user-modify 兼容性问题
 * 3.去除写死的@人之后，改用弹框发现焦点会变，获取到的光标位置不准了 已解决 用range.cloneRange()
 * 4.如果使用@按钮，而不是键盘输入的，需要确保评论区被focus 待开发
 * 5.placeholder不能过长，如果超过mention-block的宽度就不行
 */

class Comment extends React.Component {
  state = {
    showSelector: false,
    bookMark: undefined, //保存失焦前的range现场
    isBtnShow: false, //是否是按钮打开的弹框
    showPlaceHolder: true, //是否展示placeHolder
  };

  componentDidMount() {
    EventUtils.addHandler(document, "keydown", this.handleKeyDown);
  }

  componentWillUnmount() {
    EventUtils.removeHandler(document, "keydown", this.handleKeyDown);
  }

  //在光标指定位置添加文案
  //isAddTxt 是否不塞入人，而是塞入之前阻止冒泡的@符号
  processPerson = (userName, userOID, isAddTxt) => {
    //let commentDom = ReactDOM.findDOMNode(this.commentRef);
    //document.execCommand('ForeColor',false, '#0092da');
    //document.execCommand('insertText', false, 'tttttt');
    //document.execCommand确实能插入，且标记样式，但是ie中兼容性待定，还有无法插入自定义node节点，想把userOID和userName绑定来着

    const { bookMark } = this.state;
    let ele = null;
    if (isAddTxt) {
      ele = React.createElement("span", {}, `@`);
    } else {
      ele = React.createElement(
        "span",
        {
          contentEditable: false,
          className: "at-text",
          alt: JSON.stringify({
            userName,
            userOID,
          }), //想用data-info自定义属性，但是不支持
          name: "nameTag",
        },
        ` @${userName} `
      );
    }
    let frag = document.createDocumentFragment(); //创建一个虚拟的节点对象, 暂时存放那些一次插入文档的节点
    let lastNode = ReactDOM.unstable_renderSubtreeIntoContainer(this, ele, frag);
    //window.getSelection()这玩意兼容性有问题
    if (window.getSelection()) {
      let sel = window.getSelection();
      let range = bookMark ? bookMark : sel.getRangeAt(0);
      range.insertNode(frag);
      range.collapse(false);
      range.setStartAfter(lastNode); //这边不能用frag这个节点，因为会报错the given Node has no parent
      //下面这两行才能让setStartAfter起到作用
      sel.removeAllRanges();
      sel.addRange(range);
    } else if (document.getSelection()) {
      let sel = document.getSelection();
      let range = bookMark ? bookMark : sel.getRangeAt(0);
      range.insertNode(frag);
      range.collapse(false);
      range.setStartAfter(lastNode); //这边不能用frag这个节点，因为会报错the given Node has no parent
      //下面这两行才能让setStartAfter起到作用
      sel.removeAllRanges();
      sel.addRange(range);
    } else if (document.selection && document.selection.createRange()) {
      //ie 9以下用这个方案，待验证
      document.selection.createRange().pasteHTML(frag);
    }
    //document.execCommand('ForeColor', false, '#4E5B71');//这是系统字体的默认色号，主要让@完人后输入的字的色号不一致
    //这个新加的色号也会被包上nameTag
    //生成的element加个contentEditable: false就能解决上面这个问题
    this.handleChangeValue();
  };

  handleInput = (e) => {
    console.log("handle input change");
    this.handleChangeValue();
  };

  handleChangeValue = () => {
    const { onChange } = this.props;
    this.processShowPlaceHolder();
    let mentionValue = {};
    let commentRef = this.getCommentRef();
    if (commentRef) {
      mentionValue.html = commentRef.innerHTML;
      mentionValue.text = this.extractContents(commentRef.childNodes);
      mentionValue.list = this.extractList(commentRef.childNodes);
      onChange(mentionValue);
    }
  };

  extractContents = (nodes) => {
    let content = "";
    if (!nodes || nodes.length === 0) {
      return "";
    }
    if (nodes && nodes.length) {
      for (let i = 0; i < nodes.length; i++) {
        //只处理文本类型节点
        if (nodes[i].nodeType === Node.TEXT_NODE) {
          content += nodes[i].textContent || nodes[i].nodeValue;
        }
        if (nodes[i].nodeType === Node.ELEMENT_NODE) {
          content += this.extractContents(nodes[i].childNodes);
        }
      }
    }
    return content;
  };

  extractList = (nodes) => {
    let mentionList = [];
    if (!nodes || nodes.length === 0) {
      return mentionList;
    }
    if (nodes && nodes.length) {
      for (let i = 0; i < nodes.length; i++) {
        if (nodes[i].attributes && nodes[i].attributes["name"] && nodes[i].attributes["name"].value === "nameTag") {
          let user = JSON.parse(nodes[i].attributes["alt"].value);
          mentionList.push({
            userName: user.userName,
            userOID: user.userOID,
          });
        }
      }
    }
    return mentionList;
  };

  //获取comment block的dom元素，兼容IE，这里目前是需要兼容childNodes，别的属性还得再看
  getCommentRef = () => {
    //this.ref和findDOMNode一个是虚拟dom，一个是真实dom
    let commentRef = this.commentRef;
    let block = document.getElementById("comment-block");
    return commentRef || block;
  };

  handleKeyDown = (e) => {
    //let commentDom = ReactDOM.findDOMNode(this.commentRef);
    //不让输入@，@在选完人后和人名包在一个块里，便于样式特殊以及删除操作
    console.log("keyCode:" + e.keyCode);
    if (((e.keyCode === 50 || e.keyCode === 64) && e.shiftKey) || e.key === "@") {
      e.stopPropagation();
      e.preventDefault();
      this.showChooseModal();
    } else {
      setTimeout(() => {
        this.handleInput(); //keydown事件执行完之后再处理输入框变化
      }, 50);
    }
    //键盘删除健逻辑处理
    //如果这个@的人名在一行的换行处，拿到的range就不准确
    //换行的时候会自动给新的一行包上div，这个时候就不是子节点了，而是子节点的子节点，也就是removeChild会报错
    //要解决自动包上div 得先处理只允许输入文本 -webkit-user-modify: read-write-plaintext-only，这个只支持webkit内核的
    //生成的element加个contentEditable: false 这个想法绝了，删除的时候直接就整块删了，不用额外处理，兼容性待验证
    // if (e.keyCode === 8 || e.code === 'Backspace') {
    //   let sel = window.getSelection();
    //   let range = sel.getRangeAt(0);
    //   let ele = range.startContainer.parentElement;
    //   if (ele && ele.attributes.name && ele.attributes.name.value === 'nameTag') {
    //     e.stopPropagation();
    //     e.preventDefault();
    //     commentDom.removeChild(ele);
    //   }
    // }
  };

  showChooseModal = (isNeedAutoFocus) => {
    let commentRef = this.getCommentRef();
    //@按钮打开的弹框之前，输入框可能没有聚焦
    if (isNeedAutoFocus) {
      commentRef.focus();
    }
    //弹选人框前先保存现场
    let sel;
    if (window.getSelection()) {
      sel = window.getSelection();
    } else if (document.getSelection()) {
      sel = document.getSelection();
    }
    if (sel) {
      let range = sel.getRangeAt(0);
      this.setState({ bookMark: range.cloneRange() });
    }
    this.setState({ showSelector: true, isBtnShow: !!isNeedAutoFocus });
  };

  handleSelectPerson = (value) => {
    const { isBtnShow } = this.state;
    this.setState({ showSelector: false }, () => {
      if (value.result && value.result.length) {
        for (let i = 0; i < value.result.length; i++) {
          this.processPerson(value.result[i].fullName, value.result[i].userOID);
        }
      } else {
        !isBtnShow && this.processPerson(undefined, undefined, true);
      }
    });
  };

  handleCancelSelect = () => {
    const { isBtnShow } = this.state;
    this.setState({ showSelector: false }, () => {
      !isBtnShow && this.processPerson(undefined, undefined, true);
    });
  };

  processShowPlaceHolder = () => {
    let commentRef = this.getCommentRef();
    let nodes = commentRef && commentRef.childNodes;
    let text = this.extractContents(nodes);
    text = text.replace(/^\n/g, "").replace(/\n$/g, "");

    console.log("handle calculate txt");
    console.log(commentRef);
    console.log(nodes);
    console.log(text);

    //因为换行的原因，会使得childNodes与实际用户看到为空的情况不一致
    if (nodes.length === 0 || (nodes.length === 1 && text.length === 0)) {
      this.setState({ showPlaceHolder: true });
    } else {
      this.setState({ showPlaceHolder: false });
    }
  };

  //计算当前纯文本长度
  getTextLength = () => {
    let textLength = 0;
    let commentRef = this.getCommentRef();
    let text = this.extractContents(commentRef && commentRef.childNodes);
    text = text.replace(/^\n/g, "").replace(/\n$/g, "");
    textLength = text.length;
    return textLength;
  };

  render() {
    const { placeholder, companyOIDs } = this.props;
    const { showSelector, showPlaceHolder } = this.state;
    return (
      <div>
        <div className="mention-block">
          <div
            id="comment-block"
            className="comment-block"
            contentEditable="true"
            suppressContentEditableWarning="true" //去除控制台warning的显示
            ref={(el) => (this.commentRef = el)}
            //onChange={this.handleChange} //监测不到change的变化
            //onInput={this.handleInput} //ie下不支持onInput
          ></div>
          {showPlaceHolder && <div className="mentionPlaceholder">{placeholder}</div>}
          <Row style={{ marginTop: "5px" }}>
            <Col span={18}>
              <Button
                type="link"
                onClick={() => this.showChooseModal(true)}
                style={{ paddingLeft: 0, fontSize: "16px" }}
              >
                @{messages("common-1.key277") /*选人提醒查看*/}
              </Button>
              <span style={{ color: "#989898" }}>
                {messages("common-1.key278") /*若需推送消息/邮件提醒员工查看，请先@指定对象*/}
              </span>
            </Col>
            <Col span={6} style={{ textAlign: "right", color: "#989898" }}>
              {this.getTextLength()} / 500
            </Col>
          </Row>
        </div>
        <ListSelector
          visible={showSelector}
          type="user"
          showDetail
          labelKey="fullName"
          selectedData={[]}
          extraParams={{
            roleType: "TENANT",
            corporationOID: companyOIDs || undefined,
            companyOID: companyOIDs || undefined,
          }}
          onOk={this.handleSelectPerson}
          onCancel={this.handleCancelSelect}
        />
      </div>
    );
  }

  static propTypes = {
    onChange: React.PropTypes.func,
    placeholder: React.PropTypes.string,
    companyOIDs: React.PropTypes.array, //选人的公司范围
  };

  static defaultProps = {
    onChange: () => {},
    placeholder: messages("common.please.input") /*请输入*/,
    companyOIDs: [],
  };
}

export default Comment;
